from PIL import Image,ImageFont,ImageDraw        #pillow库,计算机视觉
import cv2                                       #OpenCV库,计算机视觉
import numpy as np                               #处理数值计算的库
import os                                        #os模块



class CharacterDrawingLogic:

    def gray_sort(self,text,fontPath):
        """灰度值排序(文本)返回按灰度排序后的文本"""
        gray_average = [];gray_text = []#初始化列表
        text = str(text)#把输入的字符转化为字符串(如果是数字会出错)
        font = ImageFont.truetype(fontPath,48)#设置字体
        for text_canvas in text:
            font_size_x,font_size_y = font.getsize(text_canvas)#获取文字在48大小时的长宽像素
            image = Image.new('RGB',(font_size_x,font_size_y),(255,255,255))#画布的大小(根据文字大小创建)和颜色[白色]
            draw = ImageDraw.Draw(image)#创建画布
            draw.text((0,0),text_canvas,font=font,fill='#000000')#绘制文字
            grays = []
            for i in range(image.height):#遍历图片每个像素点
                for j in range(image.width):
                    gray = image.getpixel((j,i))#返回值可能是一个int,也可能是一个三元组(x,y,z)
                    if isinstance(gray,tuple):#判断是否为元祖
                        gray = (0.2126 * gray[0] + 0.7152 * gray[1] + 0.0722 * gray[2])#RGB转灰度值的算法
                    grays.append(gray)
            gray_average.append(sum(grays)/(font_size_x*font_size_y))#调试加int方便观看(不加误差有亿点点大)
            gray_text.append(text_canvas)
        zip_gray = zip(gray_average,gray_text)#两个列表以相同顺序排列
        sorted_zip = sorted(zip_gray, key=lambda x:x[0])
        gray_average,gray_text = zip(*sorted_zip)
        text = ''.join(gray_text)#转为字符串
        return text

    def cover_text(self,text,fontPath,image_y):
        """创建底层蒙版文字图片(文本,文本字体,图片的高度(可以为None))返回生成的图像"""
        font = ImageFont.truetype(fontPath,128)#设置字体
        font_size_x,font_size_y = font.getsize(text)#获取文字在128大小时的长宽像素
        if image_y != None:#这里是给[单个字使用单个字生成]做的,可以避免文字图片文字下可能会有的空白
            font_size_y = image_y
        image = Image.new('RGB',(font_size_x,font_size_y),(255, 255, 255))#画布颜色
        draw = ImageDraw.Draw(image)
        draw.text((0,0),text,font=font,fill='#000000')#文字颜色
        return image

    def oppe_image(self,path):
        """打开图片(图片路径)"""
        path = str(path)
        image = Image.open(path)
        return image

    def zoom_image(self,image,pattern,x_y):
        """缩放图像并转换为黑白图像(图像,缩放模式,缩放数值)返回缩放后的灰度图片"""
        x_y = float(x_y)
        (x,y) = image.size
        if pattern == 0:
            x_s = int(x_y)
            y_s = int(y*x_y/x)#(y/(x/x_y))也可以(这里是用相似,然后交叉相乘推出)
        elif pattern == 1:
            x_s = int(x * x_y)
            y_s = int(y * x_y)
        image = image.resize((x_s,y_s),Image.ANTIALIAS)#缩放
        image = image.convert('L')#转为黑白图,每个像素都一个灰度值,从0到255,0是黑色,255是白色
        return image

    def image_shift_text(self,image,char_set):
        """把图片转化为文本(图像,填充文本)返回文本"""
        c,d = image.size[-1],image.size[-1]#变量c为当前转换的行数,变量d是一个寄存器
        self.text__progressbar['maximum'] = c
        self.text__progressbar['value'] = 0
        self.init_window_name.update()# 更新画面
        self.text_number__label['text'] = f'(0/{d})'

        def get_char(gray):
            if gray >= 240:
                return ' '
            else:
                return char_set[int(gray/((256.0 + 1)/len(char_set)))]
        text = ''
        for i in range(image.height):
            for j in range(image.width):
                gray = image.getpixel((j,i))#返回值可能是一个int,也可能是一个三元组(x,y,z)
                if isinstance(gray, tuple):
                    gray = int(0.2126 * gray[0] + 0.7152 * gray[1] + 0.0722 * gray[2])
                text += get_char(gray)
                if all(map(lambda c:'\u4e00' <= c <= '\u9fa5',get_char(gray))) == False:
                    text += get_char(gray)
            text += '\n'
            c -= 1
            self.text__progressbar['value'] = d - c
            self.init_window_name.update()#更新画面
            self.text_number__label['text'] = f'({d-c}/{d})'

        return text

    def save_text(self,text,wjm):
        """保存文本(文本)"""
        with open(f'./输出文件夹/{wjm}.txt','w') as f:#输出文字文件txt
            f.write(text)

    def save_image(self,text,fontPath,text_color,background_color,image):
        """生成文字图像(文本,字体,文本颜色,背景颜色,转文字前的图片)"""
        (x,y) = image.size
        font = ImageFont.truetype(fontPath,24)
        font_size_x,font_size_y = font.getsize('国')#获取文字在24大小时的长宽像素(因为'国'是全角符)
        image = Image.new('RGB',(font_size_x * x,font_size_y * y),background_color)#画布颜色
        draw = ImageDraw.Draw(image)
        draw.text((0,0),text,font=font,fill=text_color)#文字颜色
        return image

    def putout_size(self,text,fontPath_second,fontPath_fill,pattern,x_y):
        """保存的图片长宽(文本,底层字体,填充字体,缩放模式,缩放数值)"""
        font = ImageFont.truetype(fontPath_second,128)#设置字体
        font_second_size_x,font_second_size_y = font.getsize(text)#获取文字在128大小时的长宽像素
        font = ImageFont.truetype(fontPath_fill,24)
        font_fill_size_x,font_fill_size_y = font.getsize('国')#获取文字在48大小时的长宽像素
        x_y = float(x_y)
        if pattern == 0:
            x_s = int(x_y)
            y_s = int(font_second_size_y*x_y/font_second_size_x)
        elif pattern == 1:
            x_s = int(font_second_size_x * x_y)
            y_s = int(font_second_size_y * x_y)
        x = x_s * font_fill_size_x
        y = y_s * font_fill_size_y
        return x,y



    def save_video_frame(self,path):
        """提取视频帧(视频路径)"""
        cap = cv2.VideoCapture(path)
        fps = int(cap.get(7))#视频文件中的帧数
        #3为宽度,4为高度,5为帧率,7为总帧数
        c = 1
        self.video__label['text'] = '抽取视频帧:'
        self.video__progressbar['maximum'] = fps
        while True:
            ret,frame = cap.read()
            if ret:
                self.video__progressbar['value'] = c
                self.init_window_name.update()#更新画面
                self.video_number__label['text'] = f'({c}/{fps})'
                cv2.imencode('.jpg', frame)[1].tofile(f'临时文件夹/原视频帧/{c}.jpg')#这里是将截取的图像保存在本地
                c += 1
            else:
                break
        cap.release()#释放视频

    def batch_contraction_gray(self,pattern,x_y):
        """批量黑白处理并缩放(模式,缩放数值)"""
        self.video__label['text'] = '视频帧压缩处理:'
        c = len(os.listdir('./临时文件夹/原视频帧'))
        for i in range(1,c+1):
            image = Image.open(f'./临时文件夹/原视频帧/{i}.jpg')
            self.video__progressbar['value'] = i
            self.init_window_name.update()#更新画面
            self.video_number__label['text'] = f'({i}/{c})'
            image = CharacterDrawingLogic.zoom_image(self,image,pattern,x_y)
            #缩放图像并转换为黑白图像(图像,缩放模式,缩放数值)返回缩放后的灰度图片
            image.save(f'./临时文件夹/缩放黑白处理后帧/{i}.jpg')

    def batch_image_shift_text(self,char_set,fontPath):
        """批量生成文字图片(填充文本,填充字体)"""
        self.video__label['text'] = '生成文字图片:'
        c = len(os.listdir('./临时文件夹/原视频帧'))
        for i in range(1,c+1):
            image = Image.open(f'./临时文件夹/缩放黑白处理后帧/{i}.jpg')
            self.video__progressbar['value'] = i
            self.init_window_name.update()#更新画面
            self.video_number__label['text'] = f'({i}/{c})'
            text = CharacterDrawingLogic.image_shift_text(self,image,char_set)
            #把图片转化为文本(图像,填充文本)返回文本
            (x,y) = image.size
            font = ImageFont.truetype(fontPath,24)
            font_size_x,font_size_y = font.getsize('国')#获取文字在24大小时的长宽像素(因为'国'是全角符)
            image = Image.new('RGB',(font_size_x * x,font_size_y * y),'#ffffff')#画布颜色
            draw = ImageDraw.Draw(image)
            draw.text((0,0),text,font=font,fill='#000000')#文字颜色
            image.save(f'./临时文件夹/输出文本帧/{i}.jpg')

    def save_video(self,path):
        """合成图片为视频并保存(视频路径)"""
        self.video__label['text'] = '生成视频:'
        c = len(os.listdir('./临时文件夹/原视频帧'))
        cap = cv2.VideoCapture(path)
        fps = cap.get(5)    #视频帧率
        firstflag = True
        for i in range(1,c+1):
            filename = f'./临时文件夹/输出文本帧/{i}.jpg'
            self.video__progressbar['value'] = i
            self.init_window_name.update()#更新画面
            self.video_number__label['text'] = f'({i}/{c})'
            frame = cv2.imdecode(np.fromfile(filename,dtype=np.uint8),-1)
            if firstflag == True:  #读取第一张图时进行初始化
                firstflag = False
                fourcc = cv2.VideoWriter_fourcc(*'mp4v')#保存为mp4
                img_size = (frame.shape[1],frame.shape[0])
                video = cv2.VideoWriter('./临时文件夹/文字视频_没有音频.mp4',fourcc,fps,img_size)
            video.write(frame)
        video.release()#释放视频


    #ffmpeg处理操作os.system()运行命令
    # https://ffmpeg.org/ ffmpeg官网,可以通过download下载对应版本
    # https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.7z Windows平台可执行ffmpeg
    # [{os.getcwd()}/ffmpeg.exe}] 当前目录下的ffmpeg工具
    # [-threads 1] 这个可以使用多线程,线程
    # [-loglevel quiet] 关闭命令提示输出
    # [-y] 替换文件不询问

    def video_to_audio(self,video_path,audio_path):
        """提取音频(要提取音频的视频,提取的音频路径),没有音频就不会输出"""
        ffmpeg = f'{os.getcwd()}/ffmpeg.exe -i {video_path} -preset ultrafast -vn {audio_path} -loglevel quiet -y'
        os.system(ffmpeg)#运行命令

    def video_add_audio(self,video_path,add_audio_path,putout_path):
        """添加音频(要添加音频的视频,添加的音频文件,输出视频的路径)"""
        ffmpeg = f'{os.getcwd()}/ffmpeg.exe -i {video_path} -i {add_audio_path} -codec copy -shortest {putout_path} -loglevel quiet -y'
        os.system(ffmpeg)#运行命令

    def vidio_contraction(self,import_video_path,export_video_path):
        """压缩视频(要压缩的视频,输出视频路径)"""
        #压缩视频可以通过更改分辨率,帧率,码率等,这些操作ffmpeg都可以完成
        #这里选择更改码率主要是opencv视频编码文件大的主要原因的码率,所以把码率调到清晰度与文件大小较好的比,压缩10倍
        cap = cv2.VideoCapture(import_video_path)
        time = (cap.get(7)/cap.get(5))#获取视频时间单位秒
        #3为宽度,4为高度,5为帧率,7为总帧数
        cap.release()#释放视频
        size = os.path.getsize(import_video_path)#获取文件大小(单位:B)
        kbps = size/128/time#(size/1024)*8/time = size/128/time
        #码率(kbps)=文件大小(KB)*8/时间(秒)
        ffmpeg = f'{os.getcwd()}/ffmpeg.exe -i {import_video_path} -b:v {kbps/10240}M  {export_video_path} -loglevel quiet -y'
        #码率减为原来的10倍,1024*10=10240
        os.system(ffmpeg)#运行命令



###舍弃的用法,预废除###

'''


#moviepy添加音频
    def vidio_add_audio_and_contraction_moviepy(self,path,wjm):
        """压缩视频,如果有音频就添加音频,没有就压缩(视频路径,保存文件名)"""
        #from moviepy.editor import *
        #在moviepy库中导出文件会在命令提示窗口打印进度条,在gui中没有命令提示窗口会报错
        #使用(verbose=False,logger=None)可以不让它在命令提示窗口输出进度条就不会报错
        try:#有音频添加音频,使用moviepy库它会压缩
            #读取2个视频文件
            videoclip_1 = VideoFileClip(path)
            videoclip_2 = VideoFileClip('./临时文件夹/文字视频_没有音频.mp4')
            #提取原视频文件的音频部分
            audio_1 = videoclip_1.audio.write_audiofile('./临时文件夹/文字视频_音频.mp3',verbose=False,logger=None)
            #将提取的音频和文字视频进行合成
            videoclip_3 = videoclip_2.set_audio(audio_1)
            #输出新的视频文件
            videoclip_3.write_videofile(f'./输出文件夹/{wjm}.mp4',verbose=False,logger=None)
            return 0
        except:#没有音频会报错,都是特性,嘿嘿
            try:
                videoclip = VideoFileClip('./临时文件夹/文字视频_没有音频.mp4')
                videoclip.write_videofile(f'./输出文件夹/{wjm}.mp4',verbose=False,logger=None)
                return 0
            except:#这个库不是很熟
                return 1


'''